/*
 *  Command line execution tool.  Used by test cases and other manual testing.
 *
 *  For maximum portability, compile with -DDUK_CMDLINE_BAREBONES
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/resource.h>
#include <bits/pthreadtypes.h>
//#include "uv.h"
#include "duktape.h"


//extern void yjs_socket_register(duk_context *ctx);
//extern void yjs_arduino_register(duk_context *ctx);
//extern void yjs_fileio_register(duk_context *ctx);
extern void load_os_module(duk_context *ctx);
//extern void yjs_net_register(duk_context *ctx);
//extern void yjs_ipc_register(duk_context *ctx);


int interactive_mode = 0;

/* Print error to stderr and pop error. */
static void print_error(duk_context *ctx, FILE *f) {
	if (duk_is_object(ctx, -1) && duk_has_prop_string(ctx, -1, "stack")) {
		/* FIXME: print error objects specially */
		/* FIXME: pcall the string coercion */
		duk_get_prop_string(ctx, -1, "stack");
		if (duk_is_string(ctx, -1)) {
			fprintf(f, "%s\n", duk_get_string(ctx, -1));
			fflush(f);
			duk_pop_2(ctx);
			return;
		} else {
			duk_pop(ctx);
		}
	}
	duk_to_string(ctx, -1);
	fprintf(f, "%s\n", duk_get_string(ctx, -1));
	fflush(f);
	duk_pop(ctx);
}

int wrapped_compile_execute(duk_context *ctx) {
	int comp_flags;

	comp_flags = 0;
	duk_compile(ctx, comp_flags);

	duk_push_global_object(ctx);  /* 'this' binding */
	duk_call_method(ctx, 0);

	if (interactive_mode) {
		/*
		 *  In interactive mode, write to stdout so output won't interleave as easily.
		 *
		 *  NOTE: the ToString() coercion may fail in some cases; for instance,
		 *  if you evaluate:
		 *
		 *    ( {valueOf: function() {return {}}, toString: function() {return {}}});
		 *
		 *  The error is:
		 *
		 *    TypeError: failed to coerce with [[DefaultValue]]
		 *            duk_api.c:1420
		 *
		 *  This should be fixed at some point.
		 */

		duk_to_string(ctx, -1);
		fprintf(stdout, "= %s\n", duk_get_string(ctx, -1));
		fflush(stdout);
	} else {
		/* In non-interactive mode, success results are not written at all.
		 * It is important that the result value is not string coerced,
		 * as the string coercion may cause an error in some cases.
		 */
	}

	duk_pop(ctx);
	return 0;
}

int handle_fh(duk_context *ctx, FILE *f, const char *filename) {
	char *buf = NULL;
	int len;
	int got;
	int rc;
	int retval = -1;

	if (fseek(f, 0, SEEK_END) < 0) {
		goto error;
	}
	len = (int) ftell(f);
	if (fseek(f, 0, SEEK_SET) < 0) {
		goto error;
	}
	buf = (char *) malloc(len);
	if (!buf) {
		goto error;
	}

	got = fread((void *) buf, (size_t) 1, (size_t) len, f);

	duk_push_lstring(ctx, buf, got);
	duk_push_string(ctx, filename);

	free(buf);
	buf = NULL;

	interactive_mode = 0;  /* global */

	rc = duk_safe_call(ctx, wrapped_compile_execute, 2 /*nargs*/, 1 /*nret*/, DUK_INVALID_INDEX);
	if (rc != DUK_EXEC_SUCCESS) {
		print_error(ctx, stderr);
		goto error;
	} else {
		duk_pop(ctx);
		retval = 0;
	}
	/* fall thru */

 error:
	if (buf) {
		free(buf);
	}
	return retval;
}

int handle_file(duk_context *ctx, const char *filename) {
	FILE *f = NULL;
	int retval;

	f = fopen(filename, "rb");
	if (!f) {
		fprintf(stderr, "failed to open source file: %s\n", filename);
		fflush(stderr);
		goto error;
	}

	retval = handle_fh(ctx, f, filename);

	fclose(f);
	return retval;

 error:
	return -1;
}

int handle_stdin(duk_context *ctx) {
	int retval;

	retval = handle_fh(ctx, stdin, "stdin");

	return retval;
}

//int64_t counter = 0;


//void wait_for_a_while(uv_idle_t* handle, int status) {
  //  counter++;

//    if (counter >= 10e6)
//        uv_idle_stop(handle);
//}

int main(int argc, char *argv[]) {
	duk_context *ctx = NULL;
	int retval = 0;
	const char *filename = NULL;
	int interactive = 0;
	int memlimit_high = 1;
	int i;

	if(argc != 2)
	{
		printf("Usage: %s <file.js>\n", argv[0]);
		return 0;
	}

	filename = argv[1];



	ctx = duk_create_heap_default();

	jsnix_init(ctx);

	if (filename) {
		if (strcmp(filename, "-") == 0) {
			if (handle_stdin(ctx) != 0) {
				retval = 1;
				goto cleanup;
			}
		} else {
			if (handle_file(ctx, filename) != 0) {
				retval = 1;
				goto cleanup;
			}
			duk_eval_file(ctx, "modules/async.js");
		}
	}
 cleanup:


	if (ctx) {
		jsnix_clean(ctx);
		duk_destroy_heap(ctx);
	}

	return retval;
}

